Background:

Let’s take a simple model of a three department hospital, with an Emergency Department (ED), an Intensive Care Unit (ICU), and a department for General (Gen) Admission for less severe injuries. We can perform as simulation over the course of a year and see how many patients we can cure, while minimizing the amount we have to transfer, and who pass away.

Simulations are generally function-heavy. This is because it allows us perform the same operation without unnecessary copy-pasting of code. So let’s put our initial set-up of variables, which have been more-or-less arbitrarily chosen, into a function

library(dplyr)
library(ggplot2)
library(gganimate)
initial_setup   <<- function(){
  ed_beds.i       <<- 35 # Emergency Department beds available
  full_ed_beds.i  <<- 0  # Occupied Emergency Department beds
    
  icu_beds.i      <<- 50 # ICU Department beds available
  full_icu_beds.i <<- 0   # Occupied ICU Department beds
  
  gen_beds.i      <<- 50  # General Department beds available
  full_gen_beds.i <<- 0   # Occupied General Department beds
  
  total_patients  <<- 5000
  final_day       <<- 365 # final day of our simulation
  day.v           <<- seq(1:final_day)
}
initial_setup()

We’ll need a data frame to hold all of our patient data. For the sake of efficiency, we generate all of this data up front.

patient.df_setup <- function(){
  
  entry_day.v  <- sort(sample(0:final_day, total_patients, replace=TRUE)) # The day that the patient is injured
  severity.v   <- runif(n = total_patients, min = .1, max = 1) # The level of severity of patient injury
  location.v   <- rep("Home", total_patients)    # Current location of patients
  status.v     <- rep("Healthy", total_patients) # other catagories will be Injured, Released, Deceased, Cured
  placement.v  <- ifelse(severity.v >= 0.8,  "ED",
                  ifelse(severity.v > 0.6, "ICU",
                                           "Gen")) # The initial placement of patient based on severity
  
  df <- data.frame("Entry_day" = entry_day.v, "Severity" = severity.v, 
                   "Placement" = placement.v, "Location" = location.v,
                   "Status"    = status.v)
  df <- df %>% mutate(Placement = as.character(Placement), 
                                  Location  = as.character(Location),
                                  Status    = as.character(Status))
     
  df <- df %>% arrange(Entry_day)
  return(df)                         
}
patient.df <- patient.df_setup()

Now before we actually begin our simulation, let’s break it into pieces for the sake of maintainable code. This may look a bit strange at first, but it will all come together as we move toward the end.

The first function we’ll look at handles new patients entering the hospital. They are sorted into their appropriate department, and their status and location are adjusted appropriately.

entrance <- function(df, day){ # Entrance of new patients for the day
  
  df <- df %>%  mutate(Placement = ifelse(Severity >= 0.8,  "ED", ifelse(Severity > 0.6, "ICU", "Gen")),
                       Status    = ifelse((Entry_day == day) & (Location == "Home"),"Injured", Status),
                       Location  = ifelse((Status == "Injured") & (Location == "Home"),Placement, Location)) %>%
                arrange(Entry_day,Severity)
  return(df)
  }

Our second function is going to adjust our patient’s “Severity” value on a daily basis. The patient’s condition can improve, stay about the same, or deteriorate. The beta distribution has the appropriate qualities to fit our model, but it’s important to note that you could easily defend using many other distributions for this.

When we call this function later, we’re going to stay optimistic and say that patients will generally improve during their stay in the hospital.

improvements <- function(df, skew = "None"){ # Adjusts patient severity by a random value drawn from beta distribution
  # Beta distribution parameters
  if      (skew == "Left" ){param1 = 40 # Left skewed --- Patient will generally improve
                            param2 = 50} 
  else if (skew == "Right"){param1 = 50 # Right skewed --- Patient will generally deteriorate
                            param2 = 40}
  else                     {param1 = 45 # No skew --- Patient has an equal probability to improve/deteriorate
                            param2 = 45}
                  
  df <- df %>% mutate(Severity = ifelse( Location == "ED",  Severity + (rbeta(1, param1, param2) - .5) * 1.5,
                                 ifelse( Location == "ICU", Severity + (rbeta(1, param1, param2) - .5) * 1.25,
                                 ifelse( Location == "Gen", Severity + (rbeta(1, param1, param2) - .5), 
                                         Severity))),
                      Location = ifelse( Severity <= 0, "Home",  # Patient is cured and sent home.
                                 ifelse( Severity >= 1, "Morgue",# Patient passes away and is sent to the morgue
                                         Location)),
                      Status   = ifelse( Severity <= 0, "Cured", 
                                 ifelse( Severity >= 1, "Deceased", 
                                         Status)))
return(df)
}

The third and fourth functions are simply going to see how many patients are in each location, and which status they are presenting. At the end of each day, these are put into a data frame for later visualization.

tally_beds <- function(df){ # gets current number of full beds
  
  old_deceased.i    <<- deceased.i
  old_transferred.i <<- transferred.i
  
  full_ed_beds.i  <<- tally(df %>% filter(Location == "ED")) %>% as.integer()
  full_icu_beds.i <<- tally(df %>% filter(Location == "ICU")) %>% as.integer()
  full_gen_beds.i <<- tally(df %>% filter(Location == "Gen")) %>% as.integer()
  deceased.i      <<- tally(df %>% filter(Status == "Deceased")) %>% as.integer()
  cured.i         <<- tally(df %>% filter(Status == "Cured")) %>% as.integer()
  healthy.i       <<- tally(df %>% filter(Status == "Healthy")) %>% as.integer()
  transferred.i   <<- tally(df %>% filter(Status == "Transferred")) %>% as.integer()
  healhty.i       <<- tally(df %>% filter(Status == "Healthy")) %>% as.integer()
}
day_end_tally <- function(df, day){
  tally_beds(df)
  
  full_beds.df <<- rbind(full_beds.df, data.frame(Day         = day,
                                                  ED          = full_ed_beds.i,
                                                  ICU         = full_icu_beds.i, 
                                                  Gen         = full_gen_beds.i,
                                                  Healthy     = healthy.i,
                                                  Cured       = cured.i,
                                                  Deceased    = deceased.i,
                                                  Transferred = transferred.i))
}

The rerank function simply gives our lowest severity patients (per group) a rank closer to zero. Why do we need that? Because the hospital, when faced with space constraints, will try to release or transfer their lowest severity patients (with some caveats below).

rerank <- function (df){ # Ranks patient Severity by location --- lowest Severity gets lowest number rank
  df <- df %>% group_by(Location) %>%
               mutate(Rank = ifelse(Location %in% c("ED","ICU","Gen"), rank(Severity, ties.method = "first"),NA)) %>%
               ungroup()
  tally_beds(df)
  return(df)
  }

Below is our main function. It simulates one day by having new patients enter, transferring patients as needed, tallying up the beds, adjusting our patient’s severity each day, and then the function will recursively call itself until it hits the end of the simulation.

For the sake of the model, we only allow transfers to other departments if their severity is on the border between the established points. For example, a patient must be between severity level 0.8 and 0.85 to be considered for transfer from the Emergency Department to the ICU. If the hospital still can’t handle the number of patients, they are transferred to another hospital.

next_day <- function(df, day, final_day){  # Recursive function --- reassigns patients to new area if beds full
  
  df <- entrance(df, day) 
  df <- rerank(df)
  
  # reassign ED patients to ICU department if ED is full if possible
  # if patient is too severe for ICU - they are transfered to another hospital
  df <- df %>% mutate(Location = ifelse((Location == "ED")  & (Rank <= (full_ed_beds.i - ed_beds.i)),
                                 ifelse(Severity < 0.85,"ICU","Another Hospital"), Location))  
  df <- rerank(df)
  
  
  # reassign ICU patients to Gen departmentt if ICU is full
  # if patient is too severe for General admission - they are transfered to another hospital
  df <- df %>% mutate(Location = ifelse((Location == "ICU") & (Rank <= (full_icu_beds.i - icu_beds.i)),
                                 ifelse(Severity < 0.65,"Gen","Another Hospital"), Location))
  
  df <- rerank(df)
  
  # relases patients if beds are full and their severity is low enough
  # otherwise transfers them to another hospital
  df <- df %>% mutate(Location = ifelse((Location == "Gen") & (Rank <= (full_gen_beds.i - gen_beds.i)),
                                 ifelse(Severity < 0.20,"Released","Another Hospital"), Location),
                      Status   = ifelse(Location == "Another Hospital", "Transferred", Status),
                      Status   = ifelse(Location == "Released", "Released", Status))
  day_end_tally(df,day)
  df <- improvements(df, skew = "Left") # Adjusts patients severity for the day
  
  day = day + 1
  if(day <= final_day){  
    next_day (df, day, final_day) # Move on to next day if not past the final day
    }
  else {return(df)}
}

And our last function doing any analytic work will call the functions above and give us our data in a nice format for our simulation visualization.

bed_count <- function(){
  full_beds.df <<- data.frame(Day         = integer(),
                              ED          = integer(),
                              ICU         = integer(), 
                              Gen         = integer(),
                              Healthy     = integer(),
                              Cured       = integer(),
                              Deceased    = integer(),
                              Transffered = integer(),
                              Healthy     = integer())
  
  deceased.i    <<- 0
  transferred.i <<- 0
  
  patient.df <<- next_day(patient.df, 0, final_day)
  tally_beds(patient.df)
}
bed_count()
full_beds.df

Above, in tabular form, we can see how our departments and statuses of our patients change each day. But there is only so much data that can be absorbed through a table. In order for these data to be meaningful, we have to understand it on a deeper level than can be conveyed by a static number on a page.

And for that, we use visualizations. Using the gganimate package, and the data generated above, we can show how the composition of our hospital changes over the course of a year.

plot_full_beds <- function(){
  options(gganimate.nframes = max(full_beds.df$Day)*2)
  full_beds.gg <-ggplot(full_beds.df) + 
                 geom_point(aes(2, -0.3,  size = Healthy), color = "royalblue") +
                 geom_point(aes(1,   1,   size = ED),  color = "deepskyblue") + 
                 geom_point(aes(2,   1,   size = ICU), color = "deepskyblue") +
                 geom_point(aes(3,   1,   size = Gen), color = "deepskyblue") +
                 geom_point(aes(2.5, 3,   size = Deceased), color = "steelblue") +
                 geom_point(aes(2,   2,   size = Transferred), color = "steelblue") +
                 geom_point(aes(1.5, 3,   size = Cured), color = "steelblue") +
                 
                 geom_text(aes(2,  -0.5, label = paste("Healthy\n", Healthy))) + 
                 geom_text(aes(1,   0.7, label = paste("ED\n", ED))) +
                 geom_text(aes(2,   0.7, label = paste("ICU\n", ICU))) +
                 geom_text(aes(3,   0.7, label = paste("Gen\n", Gen))) +
                 geom_text(aes(2.5, 2.7, label = paste("Deceased\n", Deceased))) +             
                 geom_text(aes(2,   1.7, label = paste("Transferred\n", Transferred))) +             
                 geom_text(aes(1.5, 2.7, label = paste("Cured\n", Cured))) + 
                 
                 transition_states(Day,
                                   transition_length = 2,
                                   state_length = 2) +
                 scale_size(range = c(1,80)) +
                 theme_bw() +
                 theme(axis.line=element_blank(),
                       axis.text.x=element_blank(),
                       axis.text.y=element_blank(),
                       axis.ticks=element_blank(),
                       axis.title.x=element_blank(),
                       axis.title.y=element_blank(),
                       legend.position="none",
                       panel.background=element_blank(),
                       panel.border=element_blank(),
                       panel.grid.minor=element_blank()) +
                 xlim(c( 0.5, 3.5)) +
                 ylim(c( -1,  3.5)) +
                 ggtitle("Day {closest_state}") +
                 theme()
  print(full_beds.gg)
  anim_save("Hospital_simulation.gif", full_beds.gg)
}

plot_full_beds()

Conclusions:

The base logic of the model has wide applications, and not just in the medical field. We could take the model far out of its original purpose, and with minor modifications this could, for example, serve as a way to sort and transfer children into different classes.

Overall, this model is a simple one, and that is intentional. Many possible additions come to mind when looking at this model. We could add doctors, nurses, and administrators. We could try different amounts of beds in each area. We could add budget constraints. We could have a waiting room, or have the simulation tick away at an hourly rather than daily basis, or try different distributions for severity change, or have the ability to set up temporary triage centers for emergencies, or… any one of a million different things.

But here, the biggest constraint is not things that could be added to the simulation. Instead, the problem is the usual culprit, not enough good data. Without accurate data, we would likely soon find that a more complicated simulation would make it impossible to form proper conclusions.

LS0tCnRpdGxlOiAiSG9zcGl0YWwgRW50cnkgU2ltdWxhdGlvbiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjQmFja2dyb3VuZDoKCkxldCdzIHRha2UgYSBzaW1wbGUgbW9kZWwgb2YgYSB0aHJlZSBkZXBhcnRtZW50IGhvc3BpdGFsLCB3aXRoIGFuIEVtZXJnZW5jeSBEZXBhcnRtZW50IChFRCksIGFuIEludGVuc2l2ZSBDYXJlIFVuaXQgKElDVSksIGFuZCBhIGRlcGFydG1lbnQgZm9yIEdlbmVyYWwgKEdlbikgQWRtaXNzaW9uIGZvciBsZXNzIHNldmVyZSBpbmp1cmllcy4gV2UgY2FuIHBlcmZvcm0gYXMgc2ltdWxhdGlvbiBvdmVyIHRoZSBjb3Vyc2Ugb2YgYSB5ZWFyIGFuZCBzZWUgaG93IG1hbnkgcGF0aWVudHMgd2UgY2FuIGN1cmUsIHdoaWxlIG1pbmltaXppbmcgdGhlIGFtb3VudCB3ZSBoYXZlIHRvIHRyYW5zZmVyLCBhbmQgd2hvIHBhc3MgYXdheS4KCgpTaW11bGF0aW9ucyBhcmUgZ2VuZXJhbGx5IGZ1bmN0aW9uLWhlYXZ5LiBUaGlzIGlzIGJlY2F1c2UgaXQgYWxsb3dzIHVzIHBlcmZvcm0gdGhlIHNhbWUgb3BlcmF0aW9uIHdpdGhvdXQgdW5uZWNlc3NhcnkgY29weS1wYXN0aW5nIG9mIGNvZGUuIFNvIGxldCdzIHB1dCBvdXIgaW5pdGlhbCBzZXQtdXAgb2YgdmFyaWFibGVzLCB3aGljaCBoYXZlIGJlZW4gbW9yZS1vci1sZXNzIGFyYml0cmFyaWx5IGNob3NlbiwgaW50byBhIGZ1bmN0aW9uCgpgYGB7cn0KbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdnYW5pbWF0ZSkKCmluaXRpYWxfc2V0dXAgICA8PC0gZnVuY3Rpb24oKXsKICBlZF9iZWRzLmkgICAgICAgPDwtIDM1ICMgRW1lcmdlbmN5IERlcGFydG1lbnQgYmVkcyBhdmFpbGFibGUKICBmdWxsX2VkX2JlZHMuaSAgPDwtIDAgICMgT2NjdXBpZWQgRW1lcmdlbmN5IERlcGFydG1lbnQgYmVkcwogICAgCiAgaWN1X2JlZHMuaSAgICAgIDw8LSA1MCAjIElDVSBEZXBhcnRtZW50IGJlZHMgYXZhaWxhYmxlCiAgZnVsbF9pY3VfYmVkcy5pIDw8LSAwICAgIyBPY2N1cGllZCBJQ1UgRGVwYXJ0bWVudCBiZWRzCiAgCiAgZ2VuX2JlZHMuaSAgICAgIDw8LSA1MCAgIyBHZW5lcmFsIERlcGFydG1lbnQgYmVkcyBhdmFpbGFibGUKICBmdWxsX2dlbl9iZWRzLmkgPDwtIDAgICAjIE9jY3VwaWVkIEdlbmVyYWwgRGVwYXJ0bWVudCBiZWRzCiAgCiAgdG90YWxfcGF0aWVudHMgIDw8LSA1MDAwCiAgZmluYWxfZGF5ICAgICAgIDw8LSAzNjUgIyBmaW5hbCBkYXkgb2Ygb3VyIHNpbXVsYXRpb24KICBkYXkudiAgICAgICAgICAgPDwtIHNlcSgxOmZpbmFsX2RheSkKCn0KCmluaXRpYWxfc2V0dXAoKQpgYGAKCgpXZSdsbCBuZWVkIGEgZGF0YSBmcmFtZSB0byBob2xkIGFsbCBvZiBvdXIgcGF0aWVudCBkYXRhLiBGb3IgdGhlIHNha2Ugb2YgZWZmaWNpZW5jeSwgd2UgZ2VuZXJhdGUgYWxsIG9mIHRoaXMgZGF0YSB1cCBmcm9udC4KCmBgYHtyfQpwYXRpZW50LmRmX3NldHVwIDwtIGZ1bmN0aW9uKCl7CiAgCiAgZW50cnlfZGF5LnYgIDwtIHNvcnQoc2FtcGxlKDA6ZmluYWxfZGF5LCB0b3RhbF9wYXRpZW50cywgcmVwbGFjZT1UUlVFKSkgIyBUaGUgZGF5IHRoYXQgdGhlIHBhdGllbnQgaXMgaW5qdXJlZAogIHNldmVyaXR5LnYgICA8LSBydW5pZihuID0gdG90YWxfcGF0aWVudHMsIG1pbiA9IC4xLCBtYXggPSAxKSAjIFRoZSBsZXZlbCBvZiBzZXZlcml0eSBvZiBwYXRpZW50IGluanVyeQogIGxvY2F0aW9uLnYgICA8LSByZXAoIkhvbWUiLCB0b3RhbF9wYXRpZW50cykgICAgIyBDdXJyZW50IGxvY2F0aW9uIG9mIHBhdGllbnRzCiAgc3RhdHVzLnYgICAgIDwtIHJlcCgiSGVhbHRoeSIsIHRvdGFsX3BhdGllbnRzKSAjIG90aGVyIGNhdGFnb3JpZXMgd2lsbCBiZSBJbmp1cmVkLCBSZWxlYXNlZCwgRGVjZWFzZWQsIEN1cmVkCiAgcGxhY2VtZW50LnYgIDwtIGlmZWxzZShzZXZlcml0eS52ID49IDAuOCwgICJFRCIsCiAgICAgICAgICAgICAgICAgIGlmZWxzZShzZXZlcml0eS52ID4gMC42LCAiSUNVIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHZW4iKSkgIyBUaGUgaW5pdGlhbCBwbGFjZW1lbnQgb2YgcGF0aWVudCBiYXNlZCBvbiBzZXZlcml0eQogIAogIGRmIDwtIGRhdGEuZnJhbWUoIkVudHJ5X2RheSIgPSBlbnRyeV9kYXkudiwgIlNldmVyaXR5IiA9IHNldmVyaXR5LnYsIAogICAgICAgICAgICAgICAgICAgIlBsYWNlbWVudCIgPSBwbGFjZW1lbnQudiwgIkxvY2F0aW9uIiA9IGxvY2F0aW9uLnYsCiAgICAgICAgICAgICAgICAgICAiU3RhdHVzIiAgICA9IHN0YXR1cy52KQogIGRmIDwtIGRmICU+JSBtdXRhdGUoUGxhY2VtZW50ID0gYXMuY2hhcmFjdGVyKFBsYWNlbWVudCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTG9jYXRpb24gID0gYXMuY2hhcmFjdGVyKExvY2F0aW9uKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXR1cyAgICA9IGFzLmNoYXJhY3RlcihTdGF0dXMpKQogICAgIAogIGRmIDwtIGRmICU+JSBhcnJhbmdlKEVudHJ5X2RheSkKCiAgcmV0dXJuKGRmKSAgICAgICAgICAgICAgICAgICAgICAgICAKfQpwYXRpZW50LmRmIDwtIHBhdGllbnQuZGZfc2V0dXAoKQpgYGAKCgpOb3cgYmVmb3JlIHdlIGFjdHVhbGx5IGJlZ2luIG91ciBzaW11bGF0aW9uLCBsZXQncyBicmVhayBpdCBpbnRvIHBpZWNlcyBmb3IgdGhlIHNha2Ugb2YgbWFpbnRhaW5hYmxlIGNvZGUuIFRoaXMgbWF5IGxvb2sgYSBiaXQgc3RyYW5nZSBhdCBmaXJzdCwgYnV0IGl0IHdpbGwgYWxsIGNvbWUgdG9nZXRoZXIgYXMgd2UgbW92ZSB0b3dhcmQgdGhlIGVuZC4gCgoKVGhlIGZpcnN0IGZ1bmN0aW9uIHdlJ2xsIGxvb2sgYXQgaGFuZGxlcyBuZXcgcGF0aWVudHMgZW50ZXJpbmcgdGhlIGhvc3BpdGFsLiBUaGV5IGFyZSBzb3J0ZWQgaW50byB0aGVpciBhcHByb3ByaWF0ZSBkZXBhcnRtZW50LCBhbmQgdGhlaXIgc3RhdHVzIGFuZCBsb2NhdGlvbiBhcmUgYWRqdXN0ZWQgYXBwcm9wcmlhdGVseS4KCgpgYGB7cn0KZW50cmFuY2UgPC0gZnVuY3Rpb24oZGYsIGRheSl7ICMgRW50cmFuY2Ugb2YgbmV3IHBhdGllbnRzIGZvciB0aGUgZGF5CiAgCiAgZGYgPC0gZGYgJT4lICBtdXRhdGUoUGxhY2VtZW50ID0gaWZlbHNlKFNldmVyaXR5ID49IDAuOCwgICJFRCIsIGlmZWxzZShTZXZlcml0eSA+IDAuNiwgIklDVSIsICJHZW4iKSksCiAgICAgICAgICAgICAgICAgICAgICAgU3RhdHVzICAgID0gaWZlbHNlKChFbnRyeV9kYXkgPT0gZGF5KSAmIChMb2NhdGlvbiA9PSAiSG9tZSIpLCJJbmp1cmVkIiwgU3RhdHVzKSwKICAgICAgICAgICAgICAgICAgICAgICBMb2NhdGlvbiAgPSBpZmVsc2UoKFN0YXR1cyA9PSAiSW5qdXJlZCIpICYgKExvY2F0aW9uID09ICJIb21lIiksUGxhY2VtZW50LCBMb2NhdGlvbikpICU+JQogICAgICAgICAgICAgICAgYXJyYW5nZShFbnRyeV9kYXksU2V2ZXJpdHkpCiAgcmV0dXJuKGRmKQogIH0KYGBgCgoKT3VyIHNlY29uZCBmdW5jdGlvbiBpcyBnb2luZyB0byBhZGp1c3Qgb3VyIHBhdGllbnQncyAiU2V2ZXJpdHkiIHZhbHVlIG9uIGEgZGFpbHkgYmFzaXMuIFRoZSBwYXRpZW50J3MgY29uZGl0aW9uIGNhbiBpbXByb3ZlLCBzdGF5IGFib3V0IHRoZSBzYW1lLCBvciBkZXRlcmlvcmF0ZS4gVGhlIGJldGEgZGlzdHJpYnV0aW9uIGhhcyB0aGUgYXBwcm9wcmlhdGUgcXVhbGl0aWVzIHRvIGZpdCBvdXIgbW9kZWwsIGJ1dCBpdCdzIGltcG9ydGFudCB0byBub3RlIHRoYXQgeW91IGNvdWxkIGVhc2lseSBkZWZlbmQgdXNpbmcgbWFueSBvdGhlciBkaXN0cmlidXRpb25zIGZvciB0aGlzLgoKV2hlbiB3ZSBjYWxsIHRoaXMgZnVuY3Rpb24gbGF0ZXIsIHdlJ3JlIGdvaW5nIHRvIHN0YXkgb3B0aW1pc3RpYyBhbmQgc2F5IHRoYXQgcGF0aWVudHMgd2lsbCBnZW5lcmFsbHkgaW1wcm92ZSBkdXJpbmcgdGhlaXIgc3RheSBpbiB0aGUgaG9zcGl0YWwuCgpgYGB7cn0KaW1wcm92ZW1lbnRzIDwtIGZ1bmN0aW9uKGRmLCBza2V3ID0gIk5vbmUiKXsgIyBBZGp1c3RzIHBhdGllbnQgc2V2ZXJpdHkgYnkgYSByYW5kb20gdmFsdWUgZHJhd24gZnJvbSBiZXRhIGRpc3RyaWJ1dGlvbgoKICAjIEJldGEgZGlzdHJpYnV0aW9uIHBhcmFtZXRlcnMKICBpZiAgICAgIChza2V3ID09ICJMZWZ0IiApe3BhcmFtMSA9IDQwICMgTGVmdCBza2V3ZWQgLS0tIFBhdGllbnQgd2lsbCBnZW5lcmFsbHkgaW1wcm92ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYW0yID0gNTB9IAogIGVsc2UgaWYgKHNrZXcgPT0gIlJpZ2h0Iil7cGFyYW0xID0gNTAgIyBSaWdodCBza2V3ZWQgLS0tIFBhdGllbnQgd2lsbCBnZW5lcmFsbHkgZGV0ZXJpb3JhdGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtMiA9IDQwfQogIGVsc2UgICAgICAgICAgICAgICAgICAgICB7cGFyYW0xID0gNDUgIyBObyBza2V3IC0tLSBQYXRpZW50IGhhcyBhbiBlcXVhbCBwcm9iYWJpbGl0eSB0byBpbXByb3ZlL2RldGVyaW9yYXRlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbTIgPSA0NX0KICAgICAgICAgICAgICAgICAgCiAgZGYgPC0gZGYgJT4lIG11dGF0ZShTZXZlcml0eSA9IGlmZWxzZSggTG9jYXRpb24gPT0gIkVEIiwgIFNldmVyaXR5ICsgKHJiZXRhKDEsIHBhcmFtMSwgcGFyYW0yKSAtIC41KSAqIDEuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKCBMb2NhdGlvbiA9PSAiSUNVIiwgU2V2ZXJpdHkgKyAocmJldGEoMSwgcGFyYW0xLCBwYXJhbTIpIC0gLjUpICogMS4yNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKCBMb2NhdGlvbiA9PSAiR2VuIiwgU2V2ZXJpdHkgKyAocmJldGEoMSwgcGFyYW0xLCBwYXJhbTIpIC0gLjUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTZXZlcml0eSkpKSwKICAgICAgICAgICAgICAgICAgICAgIExvY2F0aW9uID0gaWZlbHNlKCBTZXZlcml0eSA8PSAwLCAiSG9tZSIsICAjIFBhdGllbnQgaXMgY3VyZWQgYW5kIHNlbnQgaG9tZS4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKCBTZXZlcml0eSA+PSAxLCAiTW9yZ3VlIiwjIFBhdGllbnQgcGFzc2VzIGF3YXkgYW5kIGlzIHNlbnQgdG8gdGhlIG1vcmd1ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExvY2F0aW9uKSksCiAgICAgICAgICAgICAgICAgICAgICBTdGF0dXMgICA9IGlmZWxzZSggU2V2ZXJpdHkgPD0gMCwgIkN1cmVkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZSggU2V2ZXJpdHkgPj0gMSwgIkRlY2Vhc2VkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhdHVzKSkpCgpyZXR1cm4oZGYpCn0KYGBgCgoKVGhlIHRoaXJkIGFuZCBmb3VydGggZnVuY3Rpb25zIGFyZSBzaW1wbHkgZ29pbmcgdG8gc2VlIGhvdyBtYW55IHBhdGllbnRzIGFyZSBpbiBlYWNoIGxvY2F0aW9uLCBhbmQgd2hpY2ggc3RhdHVzIHRoZXkgYXJlIHByZXNlbnRpbmcuIEF0IHRoZSBlbmQgb2YgZWFjaCBkYXksIHRoZXNlIGFyZSBwdXQgaW50byBhIGRhdGEgZnJhbWUgZm9yIGxhdGVyIHZpc3VhbGl6YXRpb24uCgoKYGBge3J9CnRhbGx5X2JlZHMgPC0gZnVuY3Rpb24oZGYpeyAjIGdldHMgY3VycmVudCBudW1iZXIgb2YgZnVsbCBiZWRzCiAgCiAgb2xkX2RlY2Vhc2VkLmkgICAgPDwtIGRlY2Vhc2VkLmkKICBvbGRfdHJhbnNmZXJyZWQuaSA8PC0gdHJhbnNmZXJyZWQuaQogIAogIGZ1bGxfZWRfYmVkcy5pICA8PC0gdGFsbHkoZGYgJT4lIGZpbHRlcihMb2NhdGlvbiA9PSAiRUQiKSkgJT4lIGFzLmludGVnZXIoKQogIGZ1bGxfaWN1X2JlZHMuaSA8PC0gdGFsbHkoZGYgJT4lIGZpbHRlcihMb2NhdGlvbiA9PSAiSUNVIikpICU+JSBhcy5pbnRlZ2VyKCkKICBmdWxsX2dlbl9iZWRzLmkgPDwtIHRhbGx5KGRmICU+JSBmaWx0ZXIoTG9jYXRpb24gPT0gIkdlbiIpKSAlPiUgYXMuaW50ZWdlcigpCiAgZGVjZWFzZWQuaSAgICAgIDw8LSB0YWxseShkZiAlPiUgZmlsdGVyKFN0YXR1cyA9PSAiRGVjZWFzZWQiKSkgJT4lIGFzLmludGVnZXIoKQogIGN1cmVkLmkgICAgICAgICA8PC0gdGFsbHkoZGYgJT4lIGZpbHRlcihTdGF0dXMgPT0gIkN1cmVkIikpICU+JSBhcy5pbnRlZ2VyKCkKICBoZWFsdGh5LmkgICAgICAgPDwtIHRhbGx5KGRmICU+JSBmaWx0ZXIoU3RhdHVzID09ICJIZWFsdGh5IikpICU+JSBhcy5pbnRlZ2VyKCkKICB0cmFuc2ZlcnJlZC5pICAgPDwtIHRhbGx5KGRmICU+JSBmaWx0ZXIoU3RhdHVzID09ICJUcmFuc2ZlcnJlZCIpKSAlPiUgYXMuaW50ZWdlcigpCiAgaGVhbGh0eS5pICAgICAgIDw8LSB0YWxseShkZiAlPiUgZmlsdGVyKFN0YXR1cyA9PSAiSGVhbHRoeSIpKSAlPiUgYXMuaW50ZWdlcigpCn0KYGBgCgpgYGB7cn0KZGF5X2VuZF90YWxseSA8LSBmdW5jdGlvbihkZiwgZGF5KXsKICB0YWxseV9iZWRzKGRmKQogIAogIGZ1bGxfYmVkcy5kZiA8PC0gcmJpbmQoZnVsbF9iZWRzLmRmLCBkYXRhLmZyYW1lKERheSAgICAgICAgID0gZGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEVEICAgICAgICAgID0gZnVsbF9lZF9iZWRzLmksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSUNVICAgICAgICAgPSBmdWxsX2ljdV9iZWRzLmksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdlbiAgICAgICAgID0gZnVsbF9nZW5fYmVkcy5pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhlYWx0aHkgICAgID0gaGVhbHRoeS5pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEN1cmVkICAgICAgID0gY3VyZWQuaSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEZWNlYXNlZCAgICA9IGRlY2Vhc2VkLmksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhbnNmZXJyZWQgPSB0cmFuc2ZlcnJlZC5pKSkKfQoKYGBgCgoKVGhlIHJlcmFuayBmdW5jdGlvbiBzaW1wbHkgZ2l2ZXMgb3VyIGxvd2VzdCBzZXZlcml0eSBwYXRpZW50cyAocGVyIGdyb3VwKSBhIHJhbmsgY2xvc2VyIHRvIHplcm8uIFdoeSBkbyB3ZSBuZWVkIHRoYXQ/IEJlY2F1c2UgdGhlIGhvc3BpdGFsLCB3aGVuIGZhY2VkIHdpdGggc3BhY2UgY29uc3RyYWludHMsIHdpbGwgdHJ5IHRvIHJlbGVhc2Ugb3IgdHJhbnNmZXIgdGhlaXIgbG93ZXN0IHNldmVyaXR5IHBhdGllbnRzICh3aXRoIHNvbWUgY2F2ZWF0cyBiZWxvdykuIAoKYGBge3J9CnJlcmFuayA8LSBmdW5jdGlvbiAoZGYpeyAjIFJhbmtzIHBhdGllbnQgU2V2ZXJpdHkgYnkgbG9jYXRpb24gLS0tIGxvd2VzdCBTZXZlcml0eSBnZXRzIGxvd2VzdCBudW1iZXIgcmFuawogIGRmIDwtIGRmICU+JSBncm91cF9ieShMb2NhdGlvbikgJT4lCiAgICAgICAgICAgICAgIG11dGF0ZShSYW5rID0gaWZlbHNlKExvY2F0aW9uICVpbiUgYygiRUQiLCJJQ1UiLCJHZW4iKSwgcmFuayhTZXZlcml0eSwgdGllcy5tZXRob2QgPSAiZmlyc3QiKSxOQSkpICU+JQogICAgICAgICAgICAgICB1bmdyb3VwKCkKICB0YWxseV9iZWRzKGRmKQogIHJldHVybihkZikKICB9CmBgYAoKCkJlbG93IGlzIG91ciBtYWluIGZ1bmN0aW9uLiBJdCBzaW11bGF0ZXMgb25lIGRheSBieSBoYXZpbmcgbmV3IHBhdGllbnRzIGVudGVyLCB0cmFuc2ZlcnJpbmcgcGF0aWVudHMgYXMgbmVlZGVkLCB0YWxseWluZyB1cCB0aGUgYmVkcywgYWRqdXN0aW5nIG91ciBwYXRpZW50J3Mgc2V2ZXJpdHkgZWFjaCBkYXksIGFuZCB0aGVuIHRoZSBmdW5jdGlvbiB3aWxsIHJlY3Vyc2l2ZWx5IGNhbGwgaXRzZWxmIHVudGlsIGl0IGhpdHMgdGhlIGVuZCBvZiB0aGUgc2ltdWxhdGlvbi4KCkZvciB0aGUgc2FrZSBvZiB0aGUgbW9kZWwsIHdlIG9ubHkgYWxsb3cgdHJhbnNmZXJzIHRvIG90aGVyIGRlcGFydG1lbnRzIGlmIHRoZWlyIHNldmVyaXR5IGlzIG9uIHRoZSBib3JkZXIgYmV0d2VlbiB0aGUgZXN0YWJsaXNoZWQgcG9pbnRzLiBGb3IgZXhhbXBsZSwgYSBwYXRpZW50IG11c3QgYmUgYmV0d2VlbiBzZXZlcml0eSBsZXZlbCAwLjggYW5kIDAuODUgdG8gYmUgY29uc2lkZXJlZCBmb3IgdHJhbnNmZXIgZnJvbSB0aGUgRW1lcmdlbmN5IERlcGFydG1lbnQgdG8gdGhlIElDVS4gSWYgdGhlIGhvc3BpdGFsIHN0aWxsIGNhbid0IGhhbmRsZSB0aGUgbnVtYmVyIG9mIHBhdGllbnRzLCB0aGV5IGFyZSB0cmFuc2ZlcnJlZCB0byBhbm90aGVyIGhvc3BpdGFsLgoKCmBgYHtyfQoKbmV4dF9kYXkgPC0gZnVuY3Rpb24oZGYsIGRheSwgZmluYWxfZGF5KXsgICMgUmVjdXJzaXZlIGZ1bmN0aW9uIC0tLSByZWFzc2lnbnMgcGF0aWVudHMgdG8gbmV3IGFyZWEgaWYgYmVkcyBmdWxsCiAgCiAgZGYgPC0gZW50cmFuY2UoZGYsIGRheSkgCiAgZGYgPC0gcmVyYW5rKGRmKQogIAogICMgcmVhc3NpZ24gRUQgcGF0aWVudHMgdG8gSUNVIGRlcGFydG1lbnQgaWYgRUQgaXMgZnVsbCBpZiBwb3NzaWJsZQogICMgaWYgcGF0aWVudCBpcyB0b28gc2V2ZXJlIGZvciBJQ1UgLSB0aGV5IGFyZSB0cmFuc2ZlcmVkIHRvIGFub3RoZXIgaG9zcGl0YWwKICBkZiA8LSBkZiAlPiUgbXV0YXRlKExvY2F0aW9uID0gaWZlbHNlKChMb2NhdGlvbiA9PSAiRUQiKSAgJiAoUmFuayA8PSAoZnVsbF9lZF9iZWRzLmkgLSBlZF9iZWRzLmkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFNldmVyaXR5IDwgMC44NSwiSUNVIiwiQW5vdGhlciBIb3NwaXRhbCIpLCBMb2NhdGlvbikpICAKICBkZiA8LSByZXJhbmsoZGYpCiAgCiAgCiAgIyByZWFzc2lnbiBJQ1UgcGF0aWVudHMgdG8gR2VuIGRlcGFydG1lbnR0IGlmIElDVSBpcyBmdWxsCiAgIyBpZiBwYXRpZW50IGlzIHRvbyBzZXZlcmUgZm9yIEdlbmVyYWwgYWRtaXNzaW9uIC0gdGhleSBhcmUgdHJhbnNmZXJlZCB0byBhbm90aGVyIGhvc3BpdGFsCiAgZGYgPC0gZGYgJT4lIG11dGF0ZShMb2NhdGlvbiA9IGlmZWxzZSgoTG9jYXRpb24gPT0gIklDVSIpICYgKFJhbmsgPD0gKGZ1bGxfaWN1X2JlZHMuaSAtIGljdV9iZWRzLmkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFNldmVyaXR5IDwgMC42NSwiR2VuIiwiQW5vdGhlciBIb3NwaXRhbCIpLCBMb2NhdGlvbikpCiAgCiAgZGYgPC0gcmVyYW5rKGRmKQogIAogICMgcmVsYXNlcyBwYXRpZW50cyBpZiBiZWRzIGFyZSBmdWxsIGFuZCB0aGVpciBzZXZlcml0eSBpcyBsb3cgZW5vdWdoCiAgIyBvdGhlcndpc2UgdHJhbnNmZXJzIHRoZW0gdG8gYW5vdGhlciBob3NwaXRhbAogIGRmIDwtIGRmICU+JSBtdXRhdGUoTG9jYXRpb24gPSBpZmVsc2UoKExvY2F0aW9uID09ICJHZW4iKSAmIChSYW5rIDw9IChmdWxsX2dlbl9iZWRzLmkgLSBnZW5fYmVkcy5pKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShTZXZlcml0eSA8IDAuMjAsIlJlbGVhc2VkIiwiQW5vdGhlciBIb3NwaXRhbCIpLCBMb2NhdGlvbiksCiAgICAgICAgICAgICAgICAgICAgICBTdGF0dXMgICA9IGlmZWxzZShMb2NhdGlvbiA9PSAiQW5vdGhlciBIb3NwaXRhbCIsICJUcmFuc2ZlcnJlZCIsIFN0YXR1cyksCiAgICAgICAgICAgICAgICAgICAgICBTdGF0dXMgICA9IGlmZWxzZShMb2NhdGlvbiA9PSAiUmVsZWFzZWQiLCAiUmVsZWFzZWQiLCBTdGF0dXMpKQoKICBkYXlfZW5kX3RhbGx5KGRmLGRheSkKICBkZiA8LSBpbXByb3ZlbWVudHMoZGYsIHNrZXcgPSAiTGVmdCIpICMgQWRqdXN0cyBwYXRpZW50cyBzZXZlcml0eSBmb3IgdGhlIGRheQogIAogIGRheSA9IGRheSArIDEKICBpZihkYXkgPD0gZmluYWxfZGF5KXsgIAogICAgbmV4dF9kYXkgKGRmLCBkYXksIGZpbmFsX2RheSkgIyBNb3ZlIG9uIHRvIG5leHQgZGF5IGlmIG5vdCBwYXN0IHRoZSBmaW5hbCBkYXkKICAgIH0KICBlbHNlIHtyZXR1cm4oZGYpfQp9CgpgYGAKCkFuZCBvdXIgbGFzdCBmdW5jdGlvbiBkb2luZyBhbnkgYW5hbHl0aWMgd29yayB3aWxsIGNhbGwgdGhlIGZ1bmN0aW9ucyBhYm92ZSBhbmQgZ2l2ZSB1cyBvdXIgZGF0YSBpbiBhIG5pY2UgZm9ybWF0IGZvciBvdXIgc2ltdWxhdGlvbiB2aXN1YWxpemF0aW9uLgoKYGBge3J9CmJlZF9jb3VudCA8LSBmdW5jdGlvbigpewogIGZ1bGxfYmVkcy5kZiA8PC0gZGF0YS5mcmFtZShEYXkgICAgICAgICA9IGludGVnZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRUQgICAgICAgICAgPSBpbnRlZ2VyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElDVSAgICAgICAgID0gaW50ZWdlcigpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR2VuICAgICAgICAgPSBpbnRlZ2VyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhlYWx0aHkgICAgID0gaW50ZWdlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDdXJlZCAgICAgICA9IGludGVnZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGVjZWFzZWQgICAgPSBpbnRlZ2VyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYW5zZmZlcmVkID0gaW50ZWdlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBIZWFsdGh5ICAgICA9IGludGVnZXIoKSkKICAKICBkZWNlYXNlZC5pICAgIDw8LSAwCiAgdHJhbnNmZXJyZWQuaSA8PC0gMAogIAogIHBhdGllbnQuZGYgPDwtIG5leHRfZGF5KHBhdGllbnQuZGYsIDAsIGZpbmFsX2RheSkKICB0YWxseV9iZWRzKHBhdGllbnQuZGYpCn0KYmVkX2NvdW50KCkKZnVsbF9iZWRzLmRmCgpgYGAKQWJvdmUsIGluIHRhYnVsYXIgZm9ybSwgd2UgY2FuIHNlZSBob3cgb3VyIGRlcGFydG1lbnRzIGFuZCBzdGF0dXNlcyBvZiBvdXIgcGF0aWVudHMgY2hhbmdlIGVhY2ggZGF5LiBCdXQgdGhlcmUgaXMgb25seSBzbyBtdWNoIGRhdGEgdGhhdCBjYW4gYmUgYWJzb3JiZWQgdGhyb3VnaCBhIHRhYmxlLiBJbiBvcmRlciBmb3IgdGhlc2UgZGF0YSB0byBiZSBtZWFuaW5nZnVsLCB3ZSBoYXZlIHRvIHVuZGVyc3RhbmQgaXQgb24gYSBkZWVwZXIgbGV2ZWwgdGhhbiBjYW4gYmUgY29udmV5ZWQgYnkgYSBzdGF0aWMgbnVtYmVyIG9uIGEgcGFnZS4KCkFuZCBmb3IgdGhhdCwgd2UgdXNlIHZpc3VhbGl6YXRpb25zLiBVc2luZyB0aGUgZ2dhbmltYXRlIHBhY2thZ2UsIGFuZCB0aGUgZGF0YSBnZW5lcmF0ZWQgYWJvdmUsIHdlIGNhbiBzaG93IGhvdyB0aGUgY29tcG9zaXRpb24gb2Ygb3VyIGhvc3BpdGFsIGNoYW5nZXMgb3ZlciB0aGUgY291cnNlIG9mIGEgeWVhci4KCmBgYHtyfQpwbG90X2Z1bGxfYmVkcyA8LSBmdW5jdGlvbigpewogIG9wdGlvbnMoZ2dhbmltYXRlLm5mcmFtZXMgPSBtYXgoZnVsbF9iZWRzLmRmJERheSkqMikKICBmdWxsX2JlZHMuZ2cgPC1nZ3Bsb3QoZnVsbF9iZWRzLmRmKSArIAogICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKDIsIC0wLjMsICBzaXplID0gSGVhbHRoeSksIGNvbG9yID0gInJveWFsYmx1ZSIpICsKICAgICAgICAgICAgICAgICBnZW9tX3BvaW50KGFlcygxLCAgIDEsICAgc2l6ZSA9IEVEKSwgIGNvbG9yID0gImRlZXBza3libHVlIikgKyAKICAgICAgICAgICAgICAgICBnZW9tX3BvaW50KGFlcygyLCAgIDEsICAgc2l6ZSA9IElDVSksIGNvbG9yID0gImRlZXBza3libHVlIikgKwogICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKDMsICAgMSwgICBzaXplID0gR2VuKSwgY29sb3IgPSAiZGVlcHNreWJsdWUiKSArCiAgICAgICAgICAgICAgICAgZ2VvbV9wb2ludChhZXMoMi41LCAzLCAgIHNpemUgPSBEZWNlYXNlZCksIGNvbG9yID0gInN0ZWVsYmx1ZSIpICsKICAgICAgICAgICAgICAgICBnZW9tX3BvaW50KGFlcygyLCAgIDIsICAgc2l6ZSA9IFRyYW5zZmVycmVkKSwgY29sb3IgPSAic3RlZWxibHVlIikgKwogICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKDEuNSwgMywgICBzaXplID0gQ3VyZWQpLCBjb2xvciA9ICJzdGVlbGJsdWUiKSArCiAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgZ2VvbV90ZXh0KGFlcygyLCAgLTAuNSwgbGFiZWwgPSBwYXN0ZSgiSGVhbHRoeVxuIiwgSGVhbHRoeSkpKSArIAogICAgICAgICAgICAgICAgIGdlb21fdGV4dChhZXMoMSwgICAwLjcsIGxhYmVsID0gcGFzdGUoIkVEXG4iLCBFRCkpKSArCiAgICAgICAgICAgICAgICAgZ2VvbV90ZXh0KGFlcygyLCAgIDAuNywgbGFiZWwgPSBwYXN0ZSgiSUNVXG4iLCBJQ1UpKSkgKwogICAgICAgICAgICAgICAgIGdlb21fdGV4dChhZXMoMywgICAwLjcsIGxhYmVsID0gcGFzdGUoIkdlblxuIiwgR2VuKSkpICsKICAgICAgICAgICAgICAgICBnZW9tX3RleHQoYWVzKDIuNSwgMi43LCBsYWJlbCA9IHBhc3RlKCJEZWNlYXNlZFxuIiwgRGVjZWFzZWQpKSkgKyAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICBnZW9tX3RleHQoYWVzKDIsICAgMS43LCBsYWJlbCA9IHBhc3RlKCJUcmFuc2ZlcnJlZFxuIiwgVHJhbnNmZXJyZWQpKSkgKyAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICBnZW9tX3RleHQoYWVzKDEuNSwgMi43LCBsYWJlbCA9IHBhc3RlKCJDdXJlZFxuIiwgQ3VyZWQpKSkgKyAKICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICB0cmFuc2l0aW9uX3N0YXRlcyhEYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhbnNpdGlvbl9sZW5ndGggPSAyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRlX2xlbmd0aCA9IDIpICsKICAgICAgICAgICAgICAgICBzY2FsZV9zaXplKHJhbmdlID0gYygxLDgwKSkgKwogICAgICAgICAgICAgICAgIHRoZW1lX2J3KCkgKwogICAgICAgICAgICAgICAgIHRoZW1lKGF4aXMubGluZT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJub25lIiwKICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5ib3JkZXI9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3I9ZWxlbWVudF9ibGFuaygpKSArCiAgICAgICAgICAgICAgICAgeGxpbShjKCAwLjUsIDMuNSkpICsKICAgICAgICAgICAgICAgICB5bGltKGMoIC0xLCAgMy41KSkgKwogICAgICAgICAgICAgICAgIGdndGl0bGUoIkRheSB7Y2xvc2VzdF9zdGF0ZX0iKSArCiAgICAgICAgICAgICAgICAgdGhlbWUoKQogIHByaW50KGZ1bGxfYmVkcy5nZykKICBhbmltX3NhdmUoIkhvc3BpdGFsX3NpbXVsYXRpb24uZ2lmIiwgZnVsbF9iZWRzLmdnKQp9CgpwbG90X2Z1bGxfYmVkcygpCgpgYGAKIyFbXShIb3NwaXRhbF9zaW11bGF0aW9uLmdpZikKCgojIyMgQ29uY2x1c2lvbnM6CgpUaGUgYmFzZSBsb2dpYyBvZiB0aGUgbW9kZWwgaGFzIHdpZGUgYXBwbGljYXRpb25zLCBhbmQgbm90IGp1c3QgaW4gdGhlIG1lZGljYWwgZmllbGQuIFdlIGNvdWxkIHRha2UgdGhlIG1vZGVsIGZhciBvdXQgb2YgaXRzIG9yaWdpbmFsIHB1cnBvc2UsIGFuZCB3aXRoIG1pbm9yIG1vZGlmaWNhdGlvbnMgdGhpcyBjb3VsZCwgZm9yIGV4YW1wbGUsIHNlcnZlIGFzIGEgd2F5IHRvIHNvcnQgYW5kIHRyYW5zZmVyIGNoaWxkcmVuIGludG8gZGlmZmVyZW50IGNsYXNzZXMuCgoKT3ZlcmFsbCwgdGhpcyBtb2RlbCBpcyBhIHNpbXBsZSBvbmUsIGFuZCB0aGF0IGlzIGludGVudGlvbmFsLiBNYW55IHBvc3NpYmxlIGFkZGl0aW9ucyBjb21lIHRvIG1pbmQgd2hlbiBsb29raW5nIGF0IHRoaXMgbW9kZWwuIFdlIGNvdWxkIGFkZCBkb2N0b3JzLCBudXJzZXMsIGFuZCBhZG1pbmlzdHJhdG9ycy4gV2UgY291bGQgdHJ5IGRpZmZlcmVudCBhbW91bnRzIG9mIGJlZHMgaW4gZWFjaCBhcmVhLiBXZSBjb3VsZCBhZGQgYnVkZ2V0IGNvbnN0cmFpbnRzLiBXZSBjb3VsZCBoYXZlIGEgd2FpdGluZyByb29tLCBvciBoYXZlIHRoZSBzaW11bGF0aW9uIHRpY2sgYXdheSBhdCBhbiBob3VybHkgcmF0aGVyIHRoYW4gZGFpbHkgYmFzaXMsIG9yIHRyeSBkaWZmZXJlbnQgZGlzdHJpYnV0aW9ucyBmb3Igc2V2ZXJpdHkgY2hhbmdlLCBvciBoYXZlIHRoZSBhYmlsaXR5IHRvIHNldCB1cCB0ZW1wb3JhcnkgdHJpYWdlIGNlbnRlcnMgZm9yIGVtZXJnZW5jaWVzLCBvci4uLiBhbnkgb25lIG9mIGEgbWlsbGlvbiBkaWZmZXJlbnQgdGhpbmdzLiAKCkJ1dCBoZXJlLCB0aGUgYmlnZ2VzdCBjb25zdHJhaW50IGlzIG5vdCB0aGluZ3MgdGhhdCBjb3VsZCBiZSBhZGRlZCB0byB0aGUgc2ltdWxhdGlvbi4gSW5zdGVhZCwgdGhlIHByb2JsZW0gaXMgdGhlIHVzdWFsIGN1bHByaXQsIG5vdCBlbm91Z2ggZ29vZCBkYXRhLiBXaXRob3V0IGFjY3VyYXRlIGRhdGEsIHdlIHdvdWxkIGxpa2VseSBzb29uIGZpbmQgdGhhdCBhIG1vcmUgY29tcGxpY2F0ZWQgc2ltdWxhdGlvbiB3b3VsZCBtYWtlIGl0ICBpbXBvc3NpYmxlIHRvIGZvcm0gcHJvcGVyIGNvbmNsdXNpb25zLgoKCgo=